

#include "lcd_internal.h"

//#define LOG     LOG_INFO0
//#define LOG3    LOG_INFO3

#ifdef LCD_DISP_PIN
#define LCD_DISP_LOW    Pad_Config( LCD_DISP_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, PAD_OUT_LOW )
#define LCD_DISP_HIGH   Pad_Config( LCD_DISP_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH )
#endif

#ifdef LCD_EXTCOMIN_PIN
#define LCD_EXTCOMIN_LOW    Pad_Config( LCD_EXTCOMIN_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, PAD_OUT_LOW )
#define LCD_EXTCOMIN_HIGH   Pad_Config( LCD_EXTCOMIN_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH )
#endif 

#ifdef LCD_EXTMODE_PIN
#define LCD_EXTMODE_LOW    Pad_Config( LCD_EXTMODE_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_DOWN, PAD_OUT_ENABLE, PAD_OUT_LOW )
#define LCD_EXTMODE_HIGH   Pad_Config( LCD_EXTMODE_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH )
#endif 

typedef struct
{
    uint8_t no;
    uint8_t data[GUI_LCD_RES_V/8];
    uint8_t dummy;
}lcd_gram_line_t;

typedef struct
{
    uint8_t cmd;
    lcd_gram_line_t lines[GUI_LCD_RES_V];
    uint8_t dummy; // end, always with 0
}lcd_gram_t;

GUI_RAM_CODE
bool lcd_write_completed(void)
{
    if( lcd_wrctl.block_write ) 
    {
        if( lcd_wrctl.offset < lcd_wrctl.size )
        {
            uint16_t remain = lcd_wrctl.size - lcd_wrctl.offset;
            if( remain > LCD_XFER_MAX_SIZE )
            {
                remain = LCD_XFER_MAX_SIZE;
            }
            LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata + lcd_wrctl.offset, remain );
            lcd_wrctl.offset += remain;
        }
        else 
        {
            lcd_wrctl.blocks.used ++;
            if( lcd_wrctl.blocks.used < lcd_wrctl.blocks.count )
            {
                // check next row 
                lcd_wrctl.pdata += lcd_wrctl.blocks.offset; // next block  
                lcd_wrctl.offset = 0; 
                uint16_t remain = lcd_wrctl.size;
                if( remain > LCD_XFER_MAX_SIZE )
                {
                    remain = LCD_XFER_MAX_SIZE; 
                }
                
                LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata, remain );
                lcd_wrctl.offset = remain; //start a block 
            }
            else 
            {
                lcd_wrctl.isbusy = false; 
//                platform_delay_us(2);
//                LCD_CS_LOW;
            }
        }
    }
    else //
    {
        if( lcd_wrctl.offset < lcd_wrctl.size )
        {
            uint16_t remain = lcd_wrctl.size - lcd_wrctl.offset;
            if( remain > LCD_XFER_MAX_SIZE )
            {
                remain = LCD_XFER_MAX_SIZE;
            }
            LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata + lcd_wrctl.offset, remain );
            lcd_wrctl.offset += remain;
        }
        else 
        {
            lcd_wrctl.isbusy = false; 
//            platform_delay_us(2);
//            LCD_CS_LOW;
        }
    }
    
    return !lcd_wrctl.isbusy;
}



static void driver_init(void)
{
    LCD_EXTMODE_LOW;
    LCD_EXTCOMIN_LOW;
    LCD_DISP_LOW;
    
    LCD_DELAY_MS(2);
    
    // clear 
    uint8_t data[2] = {0x04, 0x00 };
    lcd_write_data( data, sizeof(data) );
    
    LCD_DISP_HIGH;
}

void em_lcd_init(void)
{
    lcd_common_init();
    driver_init();
}

void em_lcd_power_off(void)
{
    LCD_DISP_LOW;
}

void em_lcd_power_on(void)
{
    lcd_gdma_init();
}
void em_lcd_sleep_out(void)
{
    lcd_gdma_init();
}
void em_lcd_turn_on(void)
{
    LCD_DISP_HIGH;
}
GUI_RAM_CODE
void em_lcd_set_window(int16_t x0, int16_t y0, int16_t w, int16_t h)
{
    // nothings to DO 
}

GUI_RAM_CODE
static void format_gram_data(lcd_gram_t* GRAM)
{
    for(int i = 0; i < GUI_LCD_RES_V; i ++ )
    {
        GRAM->lines[i].no = i;
        GRAM->lines[i].dummy = 0x00;
    }
    GRAM->dummy = 0x00;
    GRAM->cmd = 0x01;
}

GUI_RAM_CODE
void em_lcd_write(uint8_t* p_buffer, uint32_t size)
{   
    if( lcd_wrctl.isbusy )
    {
        return;
    }
    
    format_gram_data( (lcd_gram_t*)p_buffer );
    
    lcd_wrctl.isbusy = true; 
    lcd_wrctl.block_write = false;
    lcd_wrctl.pdata = (uint8_t*)p_buffer;
    lcd_wrctl.offset = 0;
    lcd_wrctl.size = size;
    
    uint32_t chunk_remain = size; 
    if( chunk_remain > LCD_XFER_MAX_SIZE )
    {
        chunk_remain = LCD_XFER_MAX_SIZE; 
    }
    lcd_wrctl.offset = chunk_remain;
    LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata, chunk_remain );
}

/*
    p_buffer -> LCD Memory 
    offset   -> next block offset 
    size     -> size of block  
    rows     -> block count 
*/
GUI_RAM_CODE
void em_lcd_write_block( uint8_t* p_buffer, int16_t offset, uint16_t size, int16_t count )
{
    if( lcd_wrctl.isbusy )
    {
        return;
    }
    
    lcd_wrctl.isbusy                = true;
    lcd_wrctl.block_write           = true; 
    lcd_wrctl.pdata                 = (uint8_t*)p_buffer;
    lcd_wrctl.offset                = 0; 
    lcd_wrctl.size                  = size; 
    
    lcd_wrctl.blocks.used           = 0;
    lcd_wrctl.blocks.size           = size;
    lcd_wrctl.blocks.count          = count;
    lcd_wrctl.blocks.offset         = offset;
    
    uint32_t chunk_remain = size; 
    if( chunk_remain > LCD_XFER_MAX_SIZE )
    {
        chunk_remain = LCD_XFER_MAX_SIZE; 
    }
    lcd_wrctl.offset                = chunk_remain; 
    LCD_SEND_DATA_ASYNC( lcd_wrctl.pdata, chunk_remain );
//    LOG_INFO1("[LCD]: block write count = %d", count );
}

//void em_lcd_draw_point( uint16_t x0, uint16_t y0, uint16_t color)
//{

//}

void em_lcd_wait_transfer_completed(void)
{
    while( lcd_wrctl.isbusy );
    em_lcd_wait_transfer_idle();
}

bool em_lcd_check_transfer_completed(void)
{
    return lcd_wrctl.isbusy; 
}

